
#ifndef __case_rpc_kenny_kfjiweur89u83r33e3e__
#define __case_rpc_kenny_kfjiweur89u83r33e3e__

#include <functional>

#include <hude/base/log.hpp>
#include <hude/rpc_json/rpc_net.hpp>

#include "test/test.hpp"

namespace test
{
    //--------------------------------------------------------------------------------
    enum : std::size_t { emCOUNT = 10U, emRESULTS = emCOUNT * 3 };

    //--------------------------------------------------------------------------------
    enum rpc_type_t { emRPC_TCP, emRPC_UDP };

    //--------------------------------------------------------------------------------
    using net_t             = hude::net::net_t<>;

    //--------------------------------------------------------------------------------
    template< rpc_type_t _type > struct rpc_trait_t {};

    template<> struct rpc_trait_t< emRPC_TCP >
    {
        using listen_t = net_t::tcp_listen_t;
        using connect_t = net_t::tcp_connect_t;
        static const char* tag() { return "<tcp>"; }
    };

    template<> struct rpc_trait_t< emRPC_UDP >
    {
        using listen_t = net_t::udp_vlink_t;
        using connect_t = net_t::udp_connect_t;
        static const char* tag() { return "<udp>"; }
    };

    //--------------------------------------------------------------------------------
    struct filter_t
    {
        void send( std::string& frame )
        {
            llog_dug( "<fileter>", "\t rpc channel send  >>>  %u: %s", frame.size(), frame.c_str() );
        }

        void recv( std::string& frame )
        {
            llog_dug( "<fileter>", "\t rpc channel recv  <<<  %u: %s", frame.size(), frame.c_str() );
        }
    };

    //--------------------------------------------------------------------------------
    template< rpc_type_t _type >
    struct test_rpc_t
    {
        using trait_t           = rpc_trait_t< _type >;
        using load_listen_t     = hude::rpc::rpc_loader_t< typename trait_t::listen_t,  filter_t >;
        using load_connect_t    = hude::rpc::rpc_loader_t< typename trait_t::connect_t, filter_t >;

        template< typename _ret >
        using caller_t = typename load_connect_t::rpc_t::template sync_call_t< _ret >;

        //--------------------------------------------------------------------------------
        int result;
        typename net_t::aio_t aio_;
        load_listen_t listener_;
        load_connect_t connector_;
        std::function< void() > on_finish_;

        //--------------------------------------------------------------------------------
        test_rpc_t( net_t::aio_t aio, std::function< void() > on_finish ) :
            aio_( aio ), on_finish_( on_finish ) {}

        //--------------------------------------------------------------------------------
        // RPC服务端
        void cb_entry_listen( typename load_listen_t::rpc_t::context_t ctx )
        {
            using rpc_t = typename load_listen_t::rpc_t;

            // 注册一个服务调用: void func1()
            {
                using cb_t = typename rpc_t::template cb_t< void() >;

                auto cb_body = []() -> void {};
                
                cb_t{ cb_body, rpc_t::type_cb_t::emMENUAL, "func1" }.add_to( ctx );
            }

            // 注册一个服务调用: int func2( int )
            {
                using cb_t = typename rpc_t::template cb_t< int(int) >;

                auto cb_body = []( int v ) -> int { return v; };
                
                cb_t{ cb_body, rpc_t::type_cb_t::emMENUAL, "func2" }.add_to( ctx );
            }

            // 注册一个服务调用: void func3( void( int ), int )
            {
                using cb_val_t = typename rpc_t::template cb_t< void( int ) >;
                using cb_t = typename rpc_t::template cb_t< void( cb_val_t, int ) >;

                auto cb_body = []( cb_val_t cb, int v ) -> void
                {
                    cb( v );
                };

                cb_t{ cb_body, rpc_t::type_cb_t::emMENUAL, "func3" }.add_to( ctx );
            }
        }

        //--------------------------------------------------------------------------------
        // RPC客户端
        void cb_entry_connect( typename load_connect_t::rpc_t::context_t ctx )
        {
            using rpc_t = typename load_connect_t::rpc_t;
            using cb_val_t = typename rpc_t::template cb_t< void( int ) >;

            auto cb_call = [=]()
            {
                {
                    this->result = 0;

                    for( int i = 0; i < emCOUNT; ++i )
                    {
                        // RPC远程调用：void func1()
                        {
                            caller_t< void >  func1 { ctx, "func1",  std::chrono::milliseconds( 50000U ) };

                            auto ret = func1( i );

                            llog_inf( trait_t::tag(), "* rpc: func1() %s", ret? "success" : "failed" );

                            if( ret ) this->result += 1;
                        }

                        // RPC远程调用：int func2( int )
                        {
                            caller_t< int >  func2 { ctx, "func2",  std::chrono::milliseconds( 50000U ) };

                            auto ret = func2( i );

                            llog_inf( trait_t::tag(), "* rpc: func2() = %d", ret? *ret : -1 );

                            if( ret ) this->result += 1;
                        }

                        // RPC远程调用：void func3( void( int ), int )
                        {
                            caller_t< int >  func3 { ctx, "func3",  std::chrono::milliseconds( 50000U ) };

                            cb_val_t on_callback{
                                []( int v ) { llog_inf( trait_t::tag(), "* rpc: func3 call: on_callback( cb( %d ), %d )", v, v ); },
                                rpc_t::type_cb_t::emMENUAL
                            };

                            auto ret = func3( on_callback, i );

                            llog_inf( trait_t::tag(), "* rpc: func3( void( %d ), %d ) = %s", i, i, ret? "success" : "failed" );

                            if( ret ) this->result += 1;
                        }
                    }
                }

                on_finish_();
            };

            std::this_thread::yield();

            aio_->task_commit( cb_call );
        }

        //--------------------------------------------------------------------------------
        void run()
        {
            enum : uint16_t { emPORT = 2222U };

            listener_  = load_listen_t { aio_ };
            connector_ = load_connect_t{ aio_ };

            listener_.boot ( "::0", emPORT, std::bind( &test_rpc_t::cb_entry_listen,  this, std::placeholders::_1 ) );
            connector_.boot( "::1", emPORT, std::bind( &test_rpc_t::cb_entry_connect, this, std::placeholders::_1 ) );
        }
    };

    //--------------------------------------------------------------------------------
    struct case_rpc_t : public hude::test::case_t
    {
        //--------------------------------------------------------------------------------
        using rpc_tcp_t = test_rpc_t< emRPC_TCP >;
        using rpc_udp_t = test_rpc_t< emRPC_UDP >;

        //--------------------------------------------------------------------------------
        bool run() override
        {
            auto aio = hude::aio::context_t<>::ctx();

            auto cb_finish = [&]()
            {
                static int cc = 0;

                cc += 1;

                if( cc > 1 ) aio->cancel();
            };

            rpc_tcp_t rpc_tcp( aio, cb_finish );
            rpc_udp_t rpc_udp( aio, cb_finish );

            auto cb_entry = []( rpc_tcp_t& tcp, rpc_udp_t& udp ) -> void
            {
                tcp.run();
                udp.run();
            };

            // 运行网络上下文的事件泵
            {
                auto ret = aio->run( std::bind( cb_entry, std::ref( rpc_tcp ), std::ref( rpc_udp ) ) );

                // 退出状态提示
                if( ret < 0 )
                {
                    llog_err( "<case>", "aio error( %d ): %s", ret, aio->error() );
                }
            }

            if( rpc_tcp.result != emRESULTS )
            {
                llog_err( "<case>", "ERROR: tcp result( %u / %u )", rpc_tcp.result, emRESULTS );
            }

            if( rpc_udp.result != emRESULTS )
            {
                llog_err( "<case>", "ERROR: udp result( %u / %u )", rpc_udp.result, emRESULTS );
            }

            return rpc_tcp.result == emRESULTS && rpc_udp.result == emRESULTS;
        }
    };

    //--------------------------------------------------------------------------------
    TEST_CASE_ADD( "rpc", case_rpc_t, false );

} // namespace test

#endif //__case_rpc_kenny_kfjiweur89u83r33e3e__


